package edu.hawaii.duedates;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import argparser.ArgParser;
import argparser.BooleanHolder;
import argparser.IntHolder;
import argparser.StringHolder;

/**
 * Checks various libraries and retrieves information on items that the patron has checked out.
 * 
 * @author Erin Kim and Arthur Shum
 */
public class DueDates {

  private static String bannerId;
  private static String lastName;
  private static String barcode = "";
  private static String phoneNumber = "";

  private static String sortBy = "library";
  private static int withinDays = 0;
  private static Boolean isVerbose = false;
  private static Boolean useMenu = false;

  private static Boolean getHsl = false;
  private static Boolean getUhm = false;

  private static List<BorrowedItem> allItems = new ArrayList<BorrowedItem>();

  /**
   * Parses user input, calls the appropriate methods to query library sites, processes the results
   * and prints out the results.
   * 
   * @param args Library sites to query, login credentials, sort/within/verbose options.
   * @throws Exception If a problem occurs while accessing a library site.
   */
  public static void main(String[] args) throws Exception {
    DueDates duedates = new DueDates();
    duedates.argparser(args);
    
    if (useMenu) {
      getMenu();
    }
    
    getAllResults();

    if (withinDays > 0) {
      filterResults();
    }

    sortResults();

    // printResults
    for (BorrowedItem item : allItems) {
      System.out.print(item.toString());
    }
  
  }
  
  /**
   * Gets the results for each library and adds them the allItems List.
   * 
   * @throws Exception If an error occurs while gathering results.
   */
  private static void getAllResults() throws Exception {
    if (getHsl) {
      List<BorrowedItem> hawaiiItems = getHawaiiResults();
      allItems.addAll(hawaiiItems);
    }
    if (getUhm) {
      List<BorrowedItem> uhItems = getUhResults();
      allItems.addAll(uhItems);
    }
  }

  /**
   * Gets checked-out item information for the patron from the Hawaii State Public Library System,
   * and stores the results in a List of BorrowedItem objects. If there are no results, displays
   * a message and returns an empty list.
   * 
   * @return A List of BorrowedItem objects, which hold information about checked-out items.
   * @throws Exception If an error occurs while getting results.
   */
  private static List<BorrowedItem> getHawaiiResults() throws Exception {
    List<BorrowedItem> hawaiiItems = new ArrayList<BorrowedItem>();
    try {
      System.out.print("Processing information for Hawaii State Public Library System ...\n\n");
      Library hsl = new HawaiiStateLibrary(barcode, phoneNumber);
      hawaiiItems = hsl.getInfo();
      if (hawaiiItems.isEmpty()) {
        System.out.print("=== No results for Hawaii State Public Library System ===\n\n");
      }
    }
    catch (Exception e) {
      System.out.print("=== Error getting results for Hawaii State Public Library System ===\n\n");
      if (isVerbose) {
        e.printStackTrace();
      }
      else {
        System.out.println(e.getMessage());
      }
      //return new ArrayList<BorrowedItem>();
    } // catch
    return hawaiiItems;
  }

  /**
   * Gets checked-out item information for the patron from the University of Hawaii Library System,
   * and stores the results in a List of BorrowedItem objects. If there are no results, displays
   * a message and returns an empty list.
   * 
   * @return A List of BorrowedItem objects, which hold information about checked-out items.
   * @throws Exception If an error occurs while getting results.
   */
  private static List<BorrowedItem> getUhResults() throws Exception {
    List<BorrowedItem> uhItems = new ArrayList<BorrowedItem>();
    try {
      System.out.print("Processing information for University of Hawaii Library System ...\n\n");
      Library uhm = new UhLibrary(bannerId, lastName);
      uhItems = uhm.getInfo();
      if (uhItems.isEmpty()) {
        System.out.print("=== No results for University of Hawaii Library System ===\n\n");
      }
    }
    catch (Exception e) {
      System.out.print("=== Error getting results for Univ. of Hawaii Library System ===\n\n");
      if (isVerbose) {
        e.printStackTrace();
      }
      else {
        System.out.println(e.getMessage());
      }
    } // catch
    return uhItems;
  }
  
  /**
   * Filters out items from the total list of results if the item's due date is greater than the
   * number of days specified by the user using the -within option.
   */
  private static void filterResults() {
    List<BorrowedItem> itemsToKeep = new ArrayList<BorrowedItem>();

    Calendar current = Calendar.getInstance();
    int currentYear = current.get(Calendar.YEAR);
    int currentMonth = current.get(Calendar.MONTH) + 1;
    int currentDay = current.get(Calendar.DAY_OF_MONTH);
    current.set(currentYear, currentMonth, currentDay);  
    long milliseconds2 = current.getTimeInMillis();
    
    for (BorrowedItem item : allItems) {
      Calendar due = Calendar.getInstance();

      String dueDateStr = item.getYyyymmddDueDate();
      int dueYear = Integer.valueOf(dueDateStr.substring(0, 4)).intValue();
      int dueMonth = Integer.valueOf(dueDateStr.substring(4, 6)).intValue();
      int dueDay = Integer.valueOf(dueDateStr.substring(6, 8)).intValue();

      due.set(dueYear, dueMonth, dueDay);
      long milliseconds1 = due.getTimeInMillis();
      long diff = milliseconds1 - milliseconds2;
      long diffDays = diff / (24 * 60 * 60 * 1000);

      if (diffDays <= withinDays) {
        itemsToKeep.add(item);
      }
    }

    // replace existing allItems List with List of itemsToKeep, or empty list if there are no
    // items to keep.
    allItems.clear();
    if (itemsToKeep.isEmpty()) {
      System.out.print("=== There are no items due within " + withinDays + " days ===\n\n");
    }
    else {
      allItems.addAll(itemsToKeep);
    }
  }

  /**
   * Sorts the results according to the user-specified option, or sorts by library by default.
   */
  private static void sortResults() {
    if ("duedate".equals(sortBy)) {
      Collections.sort(allItems, BorrowedItem.dueDateComparator);
    }
    else { // by default, sort by library
      Collections.sort(allItems, BorrowedItem.libraryComparator);
    }
  }

  /**
   * Parses arguments passed to the program, performs some validity checking, and assigns their 
   * values to the appropriate fields.
   * 
   * @param args The arguments passed to the program.
   */
  private void argparser(String[] args) {
  // This method needs to be non-static.
  // http://pmd.sourceforge.net/rules/design.html#NonThreadSafeSingleton
    ArgParser parser = new ArgParser("java -jar /path/to/DueDates.jar -<Options>");
    parser.setHelpIndentation(28);
    String[] uhm = new String[2];
    String[] hsl = new String[2];
    StringHolder sort = new StringHolder();
    IntHolder within = new IntHolder();
    BooleanHolder verbose = new BooleanHolder();
    BooleanHolder menu = new BooleanHolder();
    
    String hslOption = "-hsl %sX2 #<digits>#Barcode (10 digits) and Last 4 digits of phone #.";
    String uhmOption = "-uhm %sX2 #<params>#BannerId (8 digits) and LastName.";
    String sortOption = "-sort %s{library,duedate}#<{library|duedate}>#Sort results by " +
        "'library'(default) or by 'duedate'.";
    String withinOption = "-within %i{[0,9999]} #Only display items that are due within " +
        "<integer> days. 0 displays all items (default).";
    String verboseOption = "-verbose %v #Displays detailed error messages.";
    String menuOption = "-menu %v #Provides a menu-driven user interface. ";
      
    parser.addOption(hslOption, hsl);
    parser.addOption(uhmOption, uhm);
    parser.addOption(sortOption, sort);
    parser.addOption(withinOption, within);
    parser.addOption(verboseOption, verbose);
    parser.addOption(menuOption, menu);
    if (args.length == 0) {
      System.out.println(parser.getHelpMessage());
    }
    else {
      parser.matchAllArgs(args);
      bannerId = uhm[0];
      lastName = uhm[1];
      barcode = hsl[0];
      phoneNumber = hsl[1];
      if (bannerId != null && lastName != null) {
        getUhm = true;
      }
      if (barcode != null && phoneNumber != null) {
        getHsl = true;
      }
      sortBy = sort.value;
      withinDays = within.value;
      isVerbose = verbose.value;
      useMenu = menu.value;
      if (!getUhm && !getHsl && !useMenu) {
        System.out.println(parser.getHelpMessage());        
      }
    }
   }
  
  /*
  /**
   * Parses arguments passed to the program and assigns their values to the appropriate fields.
   * 
   * @param args The arguments passed to the program.
   */
  /*
  private static void parseArgs(String[] args) {
    // Read all the arguments.
    for (int i = 0; i < args.length; i++) {
      if ("-verbose".equals(args[i])) {
        isVerbose = true;
      }
      if ("-uhm".equals(args[i])) {
        try {
          bannerId = args[i + 1];
          lastName = args[i + 2];
          getUhm = true;
        }
        catch (ArrayIndexOutOfBoundsException a) {
          System.out.println("Error: can't read bannerId and/or lastName\n");
        }
      }
      if ("-hsl".equals(args[i])) {
        try {
          barcode = args[i + 1];
          phoneNumber = args[i + 2];
          getHsl = true;
        }
        catch (ArrayIndexOutOfBoundsException a) {
          System.out.println("Error: can't read barcode and/or phoneNumber\n");
        }
      }
      if ("-sort".equals(args[i])) {
        try {
          sortBy = args[i + 1];
          if (!"library".equals(sortBy) && !"duedate".equals(sortBy)) {
            System.out.println("Error: Invalid sort option! Sorting by library by default.\n");
          }
        }
        catch (ArrayIndexOutOfBoundsException a) {
          System.out.println("Error: can't find sort option! Sorting by library by default.\n");
        }
      }
      if ("-within".equals(args[i])) {
        try {
          withinDays = Integer.valueOf(args[i + 1]);
        }
        catch (ArrayIndexOutOfBoundsException a) {
          System.out.println("Error: can't read withinDays\n");
        }
      }
    } // for
    // if neither -uhm not -hsl are supplied
    if (!getUhm && !getHsl) {
      System.out.println("Error: specify a library\n");
      System.exit(0);
    }
  } // parseArgs()
  */

  /**
   * Provides a menu-driven user interface.
   * 
   * @throws Exception If an error occurs while querying the selected library site.
   */
  private static void getMenu() throws Exception {
    InputStreamReader in = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(in);

    System.out.println("\n******************************************");
    System.out.println("Hello and welcome to the DueDates Program");
    System.out.println("Please select a site from the following options:");
    System.out.println("1. University of Hawaii Library");
    System.out.println("2. Hawaii State Public Library");
    System.out.println("3. Exit");
    System.out.println("\n(Note: This menu doesn't support all functionality)");
    System.out.println("******************************************");
    System.out.print("Your choice: ");

    String site = br.readLine();
    Boolean exit = false;
    while (!exit) {
      if ("1".equals(site)) {

        getUhm = true;
        System.out.print("\n\nPlease enter your UH Banner ID: ");
        bannerId = br.readLine();
        System.out.print("\nPlease enter your Last name: ");
        lastName = br.readLine();
        System.out.println("\nProcessing...");

        
        // doesn't support -sort, -within, -verbose
        //getAllResults();
        /*for (BorrowedItem item : allItems) {
          System.out.print(item.toString());
        }*/
        exit = true;
      }
      else if ("2".equals(site)) {

        getHsl = true;
        System.out.print("\n\nPlease enter your barcode: ");
        barcode = br.readLine();
        System.out.print("\nPlease enter the last 4 digits of your phone number: ");
        phoneNumber = br.readLine();
        System.out.println("\nProcessing...");

        // doesn't support -sort, -within, -verbose
        //getAllResults();
        /*for (BorrowedItem item : allItems) {
          System.out.print(item.toString());
        }*/
        exit = true;
      }
      else if ("3".equals(site)) {
        System.out.println("\nThank you for using DueDates.");
        exit = true;
      }
      else {
        System.out.println("\nPlease enter a valid option...");
        System.out.print("Your choice: ");
        site = br.readLine();
      }
    } // while
  } // useMenu()
  
  /*
  /**
   * Dummy method used to stop the PMD Singleton error.
   */
  /*
  public void shutupPmd() {
    System.out.println();
  }
  */

} // class
